package com.xinmao.quantitative;

import lombok.extern.slf4j.Slf4j;
import org.ta4j.core.Rule;
import org.ta4j.core.Strategy;
import org.ta4j.core.TradingRecord;

@Slf4j
public class BaseFundStrategy implements Strategy {

    /** The name of the strategy. */
    private final String name;

    /** The entry rule. */
    private final Rule entryRule;

    /** The exit rule. */
    private final Rule exitRule;
    /**
     * The number of first bars in a bar series that this strategy ignores. During
     * the unstable bars of the strategy, any trade placement will be canceled i.e.
     * no entry/exit signal will be triggered before {@code index == unstableBars}.
     */
    private int unstableBars;

    /**
     * Constructor.
     *
     * @param entryRule the entry rule
     * @param exitRule  the exit rule
     */
    public BaseFundStrategy(Rule entryRule, Rule exitRule) {
        this(null, entryRule, exitRule, 0);
    }

    public BaseFundStrategy(Strategy strategy ) {
        this(strategy.getName(), strategy.getEntryRule(), strategy.getExitRule(), strategy.getUnstableBars());
    }

    /**
     * Constructor.
     *
     * @param entryRule    the entry rule
     * @param exitRule     the exit rule
     * @param unstableBars strategy will ignore possible signals at
     *                     {@code index < unstableBars}
     */
    public BaseFundStrategy(Rule entryRule, Rule exitRule, int unstableBars) {
        this(null, entryRule, exitRule, unstableBars);
    }

    /**
     * Constructor.
     *
     * @param name      the name of the strategy
     * @param entryRule the entry rule
     * @param exitRule  the exit rule
     */
    public BaseFundStrategy(String name, Rule entryRule, Rule exitRule) {
        this(name, entryRule, exitRule, 0);
    }

    /**
     * Constructor.
     *
     * @param name         the name of the strategy
     * @param entryRule    the entry rule
     * @param exitRule     the exit rule
     * @param unstableBars strategy will ignore possible signals at
     *                     {@code index < unstableBars}
     * @throws IllegalArgumentException if entryRule or exitRule is null
     */
    public BaseFundStrategy(String name, Rule entryRule, Rule exitRule, int unstableBars) {

        if (entryRule == null || exitRule == null) {
            throw new IllegalArgumentException("Rules cannot be null");
        }
        if (unstableBars < 0) {
            throw new IllegalArgumentException("Unstable bars must be >= 0");
        }
        this.name = name;
        this.entryRule = entryRule;
        this.exitRule = exitRule;
        this.unstableBars = unstableBars;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public Rule getEntryRule() {
        return entryRule;
    }

    @Override
    public Rule getExitRule() {
        return exitRule;
    }

    @Override
    public Strategy and(Strategy strategy) {
        return new BaseFundStrategy(name, entryRule.and(strategy.getEntryRule()), exitRule.and(strategy.getExitRule()),
                unstableBars);
    }

    @Override
    public Strategy or(Strategy strategy) {
        return new BaseFundStrategy(name, entryRule.or(strategy.getEntryRule()), exitRule.or(strategy.getExitRule()),
                unstableBars);
    }

    @Override
    public Strategy and(String s, Strategy strategy, int index) {
        return new BaseFundStrategy(name, entryRule.and(strategy.getEntryRule()), exitRule.and(strategy.getExitRule()),
                index);
    }

    @Override
    public Strategy or(String s, Strategy strategy, int index) {
        return new BaseFundStrategy(name, entryRule.or(strategy.getEntryRule()), exitRule.or(strategy.getExitRule()),
                index);
    }

    @Override
    public Strategy opposite() {
        return new BaseFundStrategy("opposite(" + name + ")", exitRule, entryRule, unstableBars);
    }

    @Override
    public void setUnstableBars(int index) {
        this.unstableBars = index;
    }

    @Override
    public int getUnstableBars() {
        return unstableBars;
    }

    @Override
    public boolean isUnstableAt(int index) {
        return index < unstableBars;
    }

    @Override
    public boolean shouldOperate(int index, TradingRecord tradingRecord) {

        boolean shouldEnter =shouldEnter(index, tradingRecord);
        boolean shouldExit = shouldExit(index, tradingRecord);

        log.info("shouldEnter:{} shouldExit: {}", shouldEnter, shouldExit);
        return shouldEnter || shouldExit;
    }

    @Override
    public boolean shouldEnter(int index) {
        return this.shouldEnter(index, null);
    }

    @Override
    public boolean shouldEnter(int index, TradingRecord tradingRecord) {
        return !isUnstableAt(index) && getEntryRule().isSatisfied(index, tradingRecord);
    }

    @Override
    public boolean shouldExit(int index) {
        return this.shouldExit(index, null);
    }

    @Override
    public boolean shouldExit(int index, TradingRecord tradingRecord) {
        return !isUnstableAt(index) && getExitRule().isSatisfied(index, tradingRecord);
    }
}
